﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Cherry.Agile.Core;
using Microsoft.Practices.Unity;
using System.Runtime.Caching;
using System.Diagnostics;

namespace Cherry.Agile.Framework.Cache
{
    /// <summary>
    /// 这个类是开放给用户对Cache进行操作的接口，所有Cache的操作都可以在这个类里找到.
    /// </summary>
    public class CacheManager : ICache
    {
        private const string _tagKey = "global::tag::{0}";


        /// <summary>
        /// 返回Cache
        /// </summary>
        static ICache __current;

        static CacheManager()
        {
            //__current = new CacheManager();
            if (Container.Current.IsRegistered<ICache>())
                __current = Container.Current.Resolve<ICache>();
            else
                __current = new CacheManager();
        }
        
        /// <summary>
        /// 定义CacheManager
        /// </summary>
        ObjectCache _impl = null;

        /// <summary>
        /// 构造函数
        /// </summary>
        private CacheManager()
        {

            _impl = MemoryCache.Default;
        }

        private CacheManager(string name)
        {
            _impl = new MemoryCache(name);
        }


        internal static CacheEntryChangeMonitor CreateChangeMonitor(CacheKey key)
        {
            var cache = MemoryCache.Default;

            var tags = key.Tags
                .Select(GetTagKey)
                .ToList();

            if (tags.Count == 0)
                return null;

            // make sure tags exist
            foreach (string tag in tags)
                cache.AddOrGetExisting(tag, DateTimeOffset.UtcNow.Ticks, ObjectCache.InfiniteAbsoluteExpiration);

            return cache.CreateCacheEntryChangeMonitor(tags);
        }

        // internal for testing
        internal static string GetKey(CacheKey cacheKey)
        {
            return cacheKey.Key;
        }

        internal static string GetTagKey(CacheTag t)
        {
            return string.Format(_tagKey, t);
        }

        internal static CacheItemPolicy CreatePolicy(CacheKey key, CachePolicy cachePolicy)
        {
            var policy = new CacheItemPolicy();

            switch (cachePolicy.Mode)
            {
                case CacheExpirationMode.Sliding:
                    policy.SlidingExpiration = cachePolicy.SlidingExpiration;
                    break;
                case CacheExpirationMode.Absolute:
                    policy.AbsoluteExpiration = cachePolicy.AbsoluteExpiration;
                    break;
                case CacheExpirationMode.Duration:
                    policy.AbsoluteExpiration = DateTimeOffset.Now.Add(cachePolicy.Duration);
                    break;
                default:
                    policy.AbsoluteExpiration = ObjectCache.InfiniteAbsoluteExpiration;
                    break;
            }

            var changeMonitor = CreateChangeMonitor(key);
            if (changeMonitor != null)
                policy.ChangeMonitors.Add(changeMonitor);

            return policy;
        }

        /// <summary>
        /// Expires the specified cache tag.
        /// </summary>
        /// <param name="cacheTag">The cache tag.</param>
        /// <returns>
        /// The number of items expired.
        /// </returns>
        public int Expire(CacheTag cacheTag)
        {
            string key = GetTagKey(cacheTag);
            var item = new CacheItem(key, DateTimeOffset.UtcNow.Ticks);
            var policy = new CacheItemPolicy { AbsoluteExpiration = ObjectCache.InfiniteAbsoluteExpiration };

            _impl.Set(item, policy);
            return 0;
        }

        /// <summary>
        /// Inserts a cache entry into the cache without overwriting any existing cache entry.
        /// </summary>
        /// <param name="cacheKey">A unique identifier for the cache entry.</param>
        /// <param name="value">The object to insert.</param>
        /// <param name="cachePolicy">An object that contains eviction details for the cache entry.</param>
        /// <returns>
        ///   <c>true</c> if insertion succeeded, or <c>false</c> if there is an already an entry in the cache that has the same key as key.
        /// </returns>
        public bool Add(CacheKey cacheKey, object value, CachePolicy cachePolicy)
        {
            string key = GetKey(cacheKey);
            var item = new CacheItem(key, value);
            var policy = CreatePolicy(cacheKey, cachePolicy);

            var existing = _impl.AddOrGetExisting(item, policy);
            return existing.Value == null;
        }


        /// <summary>
        /// Gets the cache value for the specified key
        /// </summary>
        /// <param name="cacheKey">A unique identifier for the cache entry.</param>
        /// <returns>
        /// The cache value for the specified key, if the entry exists; otherwise, <see langword="null"/>.
        /// </returns>
        public object Get(CacheKey cacheKey)
        {
            string key = GetKey(cacheKey);
            return MemoryCache.Default.Get(key);
        }

        /// <summary>
        /// Gets the cache value for the specified key that is already in the dictionary or the new value for the key as returned by <paramref name="valueFactory"/>.
        /// </summary>
        /// <param name="cacheKey">A unique identifier for the cache entry.</param>
        /// <param name="valueFactory">The function used to generate a value to insert into cache.</param>
        /// <param name="cachePolicy">A <see cref="CachePolicy"/> that contains eviction details for the cache entry.</param>
        /// <returns>
        /// The value for the key. This will be either the existing value for the key if the key is already in the cache,
        /// or the new value for the key as returned by <paramref name="valueFactory"/> if the key was not in the cache.
        /// </returns>
        public object GetOrAdd(CacheKey cacheKey, Func<CacheKey, object> valueFactory, CachePolicy cachePolicy)
        {

            var key = GetKey(cacheKey);
            var cachedResult = MemoryCache.Default.Get(key);

            if (cachedResult != null)
            {
                Debug.WriteLine("Cache Hit : " + key);
                return cachedResult;
            }

            Debug.WriteLine("Cache Miss: " + key);

            // get value and add to cache, not bothered
            // if it succeeds or not just rerturn the value
            var value = valueFactory(cacheKey);
            this.Add(cacheKey, value, cachePolicy);

            return value;
        }

        /// <summary>
        /// 获取配置节中配置缓存管理
        /// </summary>
        /// <param name="name">缓存管理名称</param>
        /// <returns>对应缓存管理的实例</returns>
        public static ICache GetCache(string name)
        {
            return new CacheManager(name);
        }

        /// <summary>
        /// 返回当前缓存实例
        /// </summary>
        public static ICache Current
        {
            get { return __current; }
        }

        /// <summary>
        /// 返回当前存储在缓存中的元素的个数
        /// </summary>
        public long Count
        {
            get
            {
                return _impl.GetCount();
            }
        }

        ///// <summary>
        ///// 增加一个CacheItem到缓存里.如果缓存中存在着和当前被增加的CacheItem相同key的item，就会先删除缓存中那个相同
        ///// key值的item，然后再加入当前被啬的CacheItem.如果在增加的过程中出现了异常，那么缓存中将不会存有当前被增加的
        ///// CacheItem.如果有这个方法增加的CacheItem是没有生命周期的即不会过期，而且优先级别是Normal.
        ///// </summary>
        ///// <param name="key">CacheItem唯一标识</param>
        ///// <param name="value">将要被存储的值，有可能为空.</param>
        ///// <exception cref="ArgumentNullException">当key为null时出现的异常</exception>
        ///// <exception cref="ArgumentException">当key为string.Empty时出现的异常</exception>
        ///// <remarks>
        ///// 不同的存储机制可以通过配置文件被指定到CacheManager，CacheManager会根据不同的存储机制把CacheItem存储到相应的存储位置
        ///// 每一个存储机制基于它们的实现体抛出它们特有的异常
        ///// </remarks>
        //public void Add(string key, object value)
        //{
        //    Add(key, value, CacheItemPriority.Default, new TimeSpan(1, 0, 0));
        //}

        /// <summary>
        /// 当缓存里存储着与key相符的item返回true
        /// </summary>
        /// <param name="key">item的唯一标识</param>
        /// <returns>当缓存里存储着与key相符的item返回true</returns>
        public bool Contains(string key)
        {
            return _impl.Contains(key);
        }

        ///// <summary>
        ///// 返回唯一标识key对应的值
        ///// </summary>
        ///// <param name="key">CacheItem的唯一标识.</param>
        ///// <returns>返回唯一标识key对应的值</returns>
        ///// <exception cref="ArgumentNullException">当key为null时出现的异常</exception>
        ///// <exception cref="ArgumentException">当key为string.Empty时出现的异常</exception>
        ///// <remarks>
        ///// 不同的存储机制可以通过配置文件被指定到CacheManager，CacheManager会根据不同的存储机制把CacheItem存储到相应的存储位置
        ///// 每一个存储机制基于它们的实现体抛出它们特有的异常
        ///// </remarks>
        //public object GetData(string key)
        //{
        //    return _impl.Get(key);
        //}

        ///// <summary>
        ///// 返回唯一标识key对应的和T类型的值
        ///// </summary>
        ///// <typeparam name="T">指定的类型</typeparam>
        ///// <param name="key">CacheItem的唯一标识.</param>
        ///// <returns>返回唯一标识key对应的和T类型的值</returns>
        ///// <exception cref="ArgumentNullException">当key为null时出现的异常</exception>
        ///// <exception cref="ArgumentException">当key为string.Empty时出现的异常</exception>
        ///// <remarks>
        ///// 不同的存储机制可以通过配置文件被指定到CacheManager，CacheManager会根据不同的存储机制把CacheItem存储到相应的存储位置
        ///// 每一个存储机制基于它们的实现体抛出它们特有的异常
        ///// </remarks>
        //public T GetData<T>(string key)
        //{
        //    return (T)GetData(key);
        //}

        ///// <summary>
        ///// 从缓存里面移除和key值相符的CacheItem，如果缓存里面找不到相应的key值的CacheItem，就不做任何移除.
        ///// </summary>
        ///// <param name="key">CacheItem的唯一标识.</param>
        ///// <exception cref="ArgumentNullException">当key为null时出现的异常</exception>
        ///// <exception cref="ArgumentException">当key为string.Empty时出现的异常</exception>
        ///// <remarks>
        ///// 不同的存储机制可以通过配置文件被指定到CacheManager，CacheManager会根据不同的存储机制把CacheItem存储到相应的存储位置
        ///// 每一个存储机制基于它们的实现体抛出它们特有的异常
        ///// </remarks>
        //public void Remove(string key)
        //{
        //    _impl.Remove(key);
        //}

        ///// <summary>
        ///// 可以让缓存无效
        ///// </summary>
        //public void Dispose()
        //{
        //    var d = _impl as IDisposable;
        //    d.Dispose();
        //}

        ///// <summary>
        ///// 从缓存中移除所有的CacheItem，如果在移除的过程中出现了错误，就不做任何移除.
        ///// </summary>
        ///// <remarks>
        ///// 不同的存储机制可以通过配置文件被指定到CacheManager，CacheManager会根据不同的存储机制把CacheItem存储到相应的存储位置
        ///// 每一个存储机制基于它们的实现体抛出它们特有的异常
        ///// </remarks>
        //public void Flush()
        //{
        //    _impl.ToList().ForEach(kv => _impl.Remove(kv.Key));
        //}

        //        /// <summary>
        ///// 请参照ICacheItemRefreshAction<see cref="ICacheItemRefreshAction"/>
        ///// </summary>
        //[Serializable]
        //class RefreshActionClass  {
        //    private Action<string, object, CacheEntryRemovedArguments> _refreshAction;

        //    public RefreshActionClass(Action<string, object, CacheEntryRemovedArguments> refreshAction) {
        //        if (_refreshAction != null)
        //            _refreshAction = refreshAction;
        //    }

        //    public void Refresh(string removedKey, object expiredValue, CacheEntryRemovedArguments removeArguments) {
        //        if (_refreshAction != null)
        //            _refreshAction(removedKey, expiredValue,removeArguments);
        //    }


        //}

        /// <summary>
        /// Inserts a cache entry into the cache without overwriting any existing cache entry.
        /// </summary>
        /// <param name="key">A unique identifier for the cache entry.</param>
        /// <param name="value">The object to insert.</param>
        /// <returns><c>true</c> if insertion succeeded, or <c>false</c> if there is an already an entry in the cache that has the same key as key.</returns>
        public bool Add(string key, object value)
        {
            var cachePolicy = new CachePolicy();
            return Add(key, value, cachePolicy);
        }

        /// <summary>
        /// Inserts a cache entry into the cache without overwriting any existing cache entry.
        /// </summary>
        /// <param name="key">A unique identifier for the cache entry.</param>
        /// <param name="value">The object to insert.</param>
        /// <param name="absoluteExpiration">The fixed date and time at which the cache entry will expire.</param>
        /// <returns>
        ///   <c>true</c> if insertion succeeded, or <c>false</c> if there is an already an entry in the cache that has the same key as key.
        /// </returns>
        public bool Add(string key, object value, DateTimeOffset absoluteExpiration)
        {
            var cachePolicy = CachePolicy.WithAbsoluteExpiration(absoluteExpiration);
            return Add(key, value, cachePolicy);
        }

        /// <summary>
        /// Inserts a cache entry into the cache without overwriting any existing cache entry.
        /// </summary>
        /// <param name="key">A unique identifier for the cache entry.</param>
        /// <param name="value">The object to insert.</param>
        /// <param name="slidingExpiration">A span of time within which a cache entry must be accessed before the cache entry is evicted from the cache.</param>
        /// <returns>
        ///   <c>true</c> if insertion succeeded, or <c>false</c> if there is an already an entry in the cache that has the same key as key.
        /// </returns>
        public bool Add(string key, object value, TimeSpan slidingExpiration)
        {
            var cachePolicy = CachePolicy.WithSlidingExpiration(slidingExpiration);
            return Add(key, value, cachePolicy);
        }

        /// <summary>
        /// Inserts a cache entry into the cache without overwriting any existing cache entry.
        /// </summary>
        /// <param name="key">A unique identifier for the cache entry.</param>
        /// <param name="value">The object to insert.</param>
        /// <param name="cachePolicy">An object that contains eviction details for the cache entry.</param>
        /// <returns>
        ///   <c>true</c> if insertion succeeded, or <c>false</c> if there is an already an entry in the cache that has the same key as key.
        /// </returns>
        public bool Add(string key, object value, CachePolicy cachePolicy)
        {
            var cacheKey = new CacheKey(key);
            return Add(cacheKey, value, cachePolicy);
        }

        /// <summary>
        /// Inserts a cache entry into the cache without overwriting any existing cache entry.
        /// </summary>
        /// <param name="cacheKey">A unique identifier for the cache entry.</param>
        /// <param name="value">The object to insert.</param>
        /// <param name="cachePolicy">An object that contains eviction details for the cache entry.</param>
        /// <returns>
        ///   <c>true</c> if insertion succeeded, or <c>false</c> if there is an already an entry in the cache that has the same key as key.
        /// </returns>
        //public virtual bool Add(CacheKey cacheKey, object value, CachePolicy cachePolicy)
        //{
        //    return Add(cacheKey, value, cachePolicy);
        //}


        /// <summary>
        /// Gets the cache value for the specified key
        /// </summary>
        /// <param name="key">A unique identifier for the cache entry.</param>
        /// <returns>The cache value for the specified key, if the entry exists; otherwise, <see langword="null"/>.</returns>
        public virtual object Get(string key)
        {
            var cacheKey = new CacheKey(key);
            return Get(cacheKey);
        }


        /// <summary>
        /// Gets the cache value for the specified key that is already in the dictionary or the new value if the key was not in the dictionary.
        /// </summary>
        /// <param name="key">A unique identifier for the cache entry.</param>
        /// <param name="value">The object to insert.</param>
        /// <returns>
        /// The value for the key. This will be either the existing value for the key if the key is already in the dictionary, 
        /// or the new value if the key was not in the dictionary.
        /// </returns>
        public object GetOrAdd(string key, object value)
        {
            var policy = new CachePolicy();
            return GetOrAdd(key, value, policy);
        }

        /// <summary>
        /// Gets the cache value for the specified key that is already in the dictionary or the new value if the key was not in the dictionary.
        /// </summary>
        /// <param name="key">A unique identifier for the cache entry.</param>
        /// <param name="value">The object to insert.</param>
        /// <param name="absoluteExpiration">The fixed date and time at which the cache entry will expire.</param>
        /// <returns>
        /// The value for the key. This will be either the existing value for the key if the key is already in the dictionary, 
        /// or the new value if the key was not in the dictionary.
        /// </returns>
        public object GetOrAdd(string key, object value, DateTimeOffset absoluteExpiration)
        {
            var policy = CachePolicy.WithAbsoluteExpiration(absoluteExpiration);
            return GetOrAdd(key, value, policy);
        }

        /// <summary>
        /// Gets the cache value for the specified key that is already in the dictionary or the new value if the key was not in the dictionary.
        /// </summary>
        /// <param name="key">A unique identifier for the cache entry.</param>
        /// <param name="value">The object to insert.</param>
        /// <param name="slidingExpiration">A span of time within which a cache entry must be accessed before the cache entry is evicted from the cache.</param>
        /// <returns>
        /// The value for the key. This will be either the existing value for the key if the key is already in the dictionary, 
        /// or the new value if the key was not in the dictionary.
        /// </returns>
        public object GetOrAdd(string key, object value, TimeSpan slidingExpiration)
        {
            var policy = CachePolicy.WithSlidingExpiration(slidingExpiration);
            return GetOrAdd(key, value, policy);
        }

        /// <summary>
        /// Gets the cache value for the specified key that is already in the dictionary or the new value if the key was not in the dictionary.
        /// </summary>
        /// <param name="key">A unique identifier for the cache entry.</param>
        /// <param name="value">The object to insert.</param>
        /// <param name="cachePolicy">An object that contains eviction details for the cache entry.</param>
        /// <returns>
        /// The value for the key. This will be either the existing value for the key if the key is already in the dictionary, 
        /// or the new value if the key was not in the dictionary.
        /// </returns>
        public object GetOrAdd(string key, object value, CachePolicy cachePolicy)
        {
            var cacheKey = new CacheKey(key);
            return GetOrAdd(cacheKey, value, cachePolicy);
        }

        /// <summary>
        /// Gets the cache value for the specified key that is already in the dictionary or the new value if the key was not in the dictionary.
        /// </summary>
        /// <param name="key">A unique identifier for the cache entry.</param>
        /// <param name="value">The object to insert.</param>
        /// <param name="cachePolicy">An object that contains eviction details for the cache entry.</param>
        /// <returns>
        /// The value for the key. This will be either the existing value for the key if the key is already in the dictionary, 
        /// or the new value if the key was not in the dictionary.
        /// </returns>
        public object GetOrAdd(CacheKey key, object value, CachePolicy cachePolicy)
        {
            return GetOrAdd(key, k => value, cachePolicy);
        }


        /// <summary>
        /// Gets the cache value for the specified key that is already in the dictionary or the new value for the key as returned by <paramref name="valueFactory"/>.
        /// </summary>
        /// <param name="key">A unique identifier for the cache entry.</param>
        /// <param name="valueFactory">The function used to generate a value to insert into cache.</param>
        /// <returns>
        /// The value for the key. This will be either the existing value for the key if the key is already in the dictionary, 
        /// or the new value for the key as returned by <paramref name="valueFactory"/> if the key was not in the dictionary.
        /// </returns>
        public object GetOrAdd(string key, Func<string, object> valueFactory)
        {
            return GetOrAdd(key, valueFactory, new CachePolicy());
        }

        /// <summary>
        /// Gets the cache value for the specified key that is already in the dictionary or the new value for the key as returned by <paramref name="valueFactory"/>.
        /// </summary>
        /// <param name="key">A unique identifier for the cache entry.</param>
        /// <param name="valueFactory">The function used to generate a value to insert into cache.</param>
        /// <param name="absoluteExpiration">The fixed date and time at which the cache entry will expire.</param>
        /// <returns>
        /// The value for the key. This will be either the existing value for the key if the key is already in the dictionary, 
        /// or the new value for the key as returned by <paramref name="valueFactory"/> if the key was not in the dictionary.
        /// </returns>
        public object GetOrAdd(string key, Func<string, object> valueFactory, DateTimeOffset absoluteExpiration)
        {
            var policy = CachePolicy.WithAbsoluteExpiration(absoluteExpiration);
            return GetOrAdd(key, valueFactory, policy);
        }

        /// <summary>
        /// Gets the cache value for the specified key that is already in the dictionary or the new value for the key as returned by <paramref name="valueFactory"/>.
        /// </summary>
        /// <param name="key">A unique identifier for the cache entry.</param>
        /// <param name="valueFactory">The function used to generate a value to insert into cache.</param>
        /// <param name="slidingExpiration">A span of time within which a cache entry must be accessed before the cache entry is evicted from the cache.</param>
        /// <returns>
        /// The value for the key. This will be either the existing value for the key if the key is already in the dictionary, 
        /// or the new value for the key as returned by <paramref name="valueFactory"/> if the key was not in the dictionary.
        /// </returns>
        public object GetOrAdd(string key, Func<string, object> valueFactory, TimeSpan slidingExpiration)
        {
            var policy = CachePolicy.WithSlidingExpiration(slidingExpiration);
            return GetOrAdd(key, valueFactory, policy);
        }

        /// <summary>
        /// Gets the cache value for the specified key that is already in the dictionary or the new value for the key as returned by <paramref name="valueFactory"/>.
        /// </summary>
        /// <param name="key">A unique identifier for the cache entry.</param>
        /// <param name="valueFactory">The function used to generate a value to insert into cache.</param>
        /// <param name="cachePolicy">An object that contains eviction details for the cache entry.</param>
        /// <returns>
        /// The value for the key. This will be either the existing value for the key if the key is already in the dictionary, 
        /// or the new value for the key as returned by <paramref name="valueFactory"/> if the key was not in the dictionary.
        /// </returns>
        public object GetOrAdd(string key, Func<string, object> valueFactory, CachePolicy cachePolicy)
        {
            var cacheKey = new CacheKey(key);
            return GetOrAdd(cacheKey, valueFactory, cachePolicy);
        }

        ///// <summary>
        ///// Gets the cache value for the specified key that is already in the dictionary or the new value for the key as returned by <paramref name="valueFactory"/>.
        ///// </summary>
        ///// <param name="cacheKey">A unique identifier for the cache entry.</param>
        ///// <param name="valueFactory">The function used to generate a value to insert into cache.</param>
        ///// <param name="cachePolicy">An object that contains eviction details for the cache entry.</param>
        ///// <returns>
        ///// The value for the key. This will be either the existing value for the key if the key is already in the dictionary, 
        ///// or the new value for the key as returned by <paramref name="valueFactory"/> if the key was not in the dictionary.
        ///// </returns>
        //public virtual object GetOrAdd(CacheKey cacheKey, Func<CacheKey, object> valueFactory, CachePolicy cachePolicy)
        //{
        //    return GetOrAdd(cacheKey, valueFactory, cachePolicy);;
        //}

#if net45
        /// <summary>
        /// Gets the cache value for the specified key that is already in the dictionary or the new value for the key as returned asynchronously by <paramref name="valueFactory"/>.
        /// </summary>
        /// <param name="cacheKey">A unique identifier for the cache entry.</param>
        /// <param name="valueFactory">The asynchronous function used to generate a value to insert into cache.</param>
        /// <param name="cachePolicy">An object that contains eviction details for the cache entry.</param>
        /// <returns>
        /// The value for the key. This will be either the existing value for the key if the key is already in the dictionary, 
        /// or the new value for the key as returned by <paramref name="valueFactory"/> if the key was not in the dictionary.
        /// </returns>
        public virtual Task<object> GetOrAddAsync(CacheKey cacheKey, Func<CacheKey, Task<object>> valueFactory, CachePolicy cachePolicy)
        {
            var provider = ResolveProvider();
            var item = provider.GetOrAddAsync(cacheKey, valueFactory, cachePolicy);

            return item;
        }
#endif

        /// <summary>
        /// Removes a cache entry from the cache. 
        /// </summary>
        /// <param name="key">A unique identifier for the cache entry.</param>
        /// <returns>If the entry is found in the cache, the removed cache entry; otherwise, <see langword="null"/>.</returns>
        public object Remove(string key)
        {
            var cacheKey = new CacheKey(key);
            return Remove(cacheKey);
        }

        /// <summary>
        /// Removes a cache entry from the cache. 
        /// </summary>
        /// <param name="cacheKey">A unique identifier for the cache entry.</param>
        /// <returns>If the entry is found in the cache, the removed cache entry; otherwise, <see langword="null"/>.</returns>
        public virtual object Remove(CacheKey cacheKey)
        {
            return Remove(cacheKey);
        }


        /// <summary>
        /// Expires the specified cache tag.
        /// </summary>
        /// <param name="tag">The cache tag.</param>
        /// <returns></returns>
        public int Expire(string tag)
        {
            var cacheTag = new CacheTag(tag);
            return Expire(cacheTag);
        }


        /// <summary>
        /// Inserts a cache entry into the cache overwriting any existing cache entry.
        /// </summary>
        /// <param name="key">A unique identifier for the cache entry.</param>
        /// <param name="value">The object to insert.</param>
        public void Set(string key, object value)
        {
            var policy = new CachePolicy();
            Set(key, value, policy);
        }

        /// <summary>
        /// Inserts a cache entry into the cache overwriting any existing cache entry.
        /// </summary>
        /// <param name="key">A unique identifier for the cache entry.</param>
        /// <param name="value">The object to insert.</param>
        /// <param name="absoluteExpiration">The fixed date and time at which the cache entry will expire.</param>
        public void Set(string key, object value, DateTimeOffset absoluteExpiration)
        {
            var policy = CachePolicy.WithAbsoluteExpiration(absoluteExpiration);
            Set(key, value, policy);
        }

        /// <summary>
        /// Inserts a cache entry into the cache overwriting any existing cache entry.
        /// </summary>
        /// <param name="key">A unique identifier for the cache entry.</param>
        /// <param name="value">The object to insert.</param>
        /// <param name="slidingExpiration">A span of time within which a cache entry must be accessed before the cache entry is evicted from the cache.</param>
        public void Set(string key, object value, TimeSpan slidingExpiration)
        {
            var policy = CachePolicy.WithSlidingExpiration(slidingExpiration);
            Set(key, value, policy);
        }

        /// <summary>
        /// Inserts a cache entry into the cache overwriting any existing cache entry.
        /// </summary>
        /// <param name="key">A unique identifier for the cache entry.</param>
        /// <param name="value">The object to insert.</param>
        /// <param name="cachePolicy">An object that contains eviction details for the cache entry.</param>
        public void Set(string key, object value, CachePolicy cachePolicy)
        {
            var cacheKey = new CacheKey(key);
            Set(cacheKey, value, cachePolicy);
        }

        /// <summary>
        /// Inserts a cache entry into the cache overwriting any existing cache entry.
        /// </summary>
        /// <param name="cacheKey">A unique identifier for the cache entry.</param>
        /// <param name="value">The object to insert.</param>
        /// <param name="cachePolicy">An object that contains eviction details for the cache entry.</param>
        public virtual void Set(CacheKey cacheKey, object value, CachePolicy cachePolicy)
        {
            Set(cacheKey, value, cachePolicy);
        }
       
    }
}
